import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcryptjs from 'bcryptjs';
import to from 'await-to-js';
import { JwtService } from '@nestjs/jwt';
import { User } from './entities/user.entity';
import { VerificationHistory } from './entities/verificationHistory.entity';
import { OperationType } from '../shared/enum';
import { DEFAULT_ROLES, EXPIRES_TIME, SYS_NAME_EN } from '../shared/constants';
import { SendEmailService } from '../shared/sendEmail.service';
import { RedisService } from '../shared/redis.service';
import { CreateUserDto, ResetPasswordDto, UpdateUserDto } from './dto';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    @InjectRepository(VerificationHistory)
    private verificationHistoryRepository: Repository<VerificationHistory>,
    private readonly JwtService: JwtService,
    private readonly sendEmailService: SendEmailService,
    private readonly redisService: RedisService,
  ) {}

  /* 创建用户 */
  async create(data: CreateUserDto) {
    console.log(data);
    // 1. 校验用户名是否存在
    const userRecord = await this.userRepository.findOne({
      where: { username: data.username },
    });
    if (userRecord) {
      throw new Error('账号已存在');
    }
    // 2. 校验验证码 有效性
    const record = await this.verificationHistoryRepository.findOne({
      where: {
        username: data.username,
        operationType: OperationType.register,
        isUsed: false,
      },
      order: { created_at: 'DESC' }, // 按 created_at 降序排列
    });
    if (
      !record ||
      record.verificationCode != data.captcha ||
      !isExpired(record.expiresAt)
    )
      throw new Error('验证码不正确或已过期');
    // 3. 对密码进行加密处理, 随机盐长度设为12
    data.password = bcryptjs.hashSync(data.password, 12);
    //4. 对用户赋予默认角色
    data.roles = JSON.stringify(DEFAULT_ROLES) as any;
    //5. 注册用户
    const user = await this.userRepository.create(data as any);
    let result = await this.userRepository.save(user);
    //6. 验证码标识为已使用
    await this.verificationHistoryRepository.update(record.id, {
      isUsed: true,
    });
    // 6. 返回用户信息(屏蔽敏感信息)
    result = blockSensitiveInfo(result);
    return result;
  }

  /* 发送邮件 */
  async sendCaptcha(
    email: string,
    operationType: OperationType = OperationType.register,
    ipAddress: string = '',
  ) {
    // 1.邮箱校验
    const emailRegex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
    if (!emailRegex.test(email)) throw new Error('邮箱格式不正确');
    // 2. 随机获取验证码
    const captcha = this.sendEmailService.generateRandomCode();
    // 3. 发送
    const title = `护理知识图谱问答平台-${
      operationType == OperationType.register ? '注册' : '密码找回'
    }通知`;
    const content = `本次注册的验证码为<span style="margin: 0 8px; font-size: 16px;color: #fc3c49">${captcha}</span>，有效时间为15分钟！`;
    const [err] = await to<any>(
      this.sendEmailService.sendCaptchaToEmail(email, title, content),
    );
    if (err) throw new Error('发送失败');
    // 4. 存储进数据库
    const post = this.verificationHistoryRepository.create({
      username: email,
      operationType: operationType,
      verificationCode: captcha,
      expiresAt: new Date().getTime() + EXPIRES_TIME + '',
      isUsed: false,
      ipAddress,
    });
    this.verificationHistoryRepository.save(post);

    return {
      message: '发送成功',
    };
  }

  // 登录
  async login(data: Partial<User>) {
    // 1. 校验用户名是否存在
    const userRecord = await this.userRepository.findOne({
      where: { username: data.username },
    });
    if (!userRecord) throw new Error('账号不存在');
    // 2. 对比密码
    const compareRes: boolean = bcryptjs.compareSync(
      data.password,
      userRecord.password,
    );
    if (!compareRes) throw new Error('账号或者密码不正确');

    // 3. 生成token
    const roles =
      (userRecord.roles && JSON.parse(userRecord.roles)) || DEFAULT_ROLES;

    const payload = {
      userId: userRecord.id,
      username: userRecord.username,
      roles,
    };
    console.log(payload);

    const token = this.JwtService.sign(payload);

    // 4. 记录token，做状态管理(redis)
    await this.redisService.setWithExpiry(
      `${SYS_NAME_EN}-${userRecord.id}-${userRecord.username}`,
      token,
    );

    return { token };
  }

  // 根据id获取用户信息
  async getUserInfoById(id: number) {
    // 1. 校验用户是否存在
    const userRecord = await this.userRepository.findOne({
      where: { id: id },
    });
    if (!userRecord) throw new Error('用户不存在');
    //2. 数据格式装换
    try {
      userRecord.roles = JSON.parse(userRecord.roles);
    } catch (error) {
      console.log(error);
    }
    //2.去除敏感信息
    return blockSensitiveInfo(userRecord);
  }

  // 删除用户(批量)
  async deleteUserById(idList: number[]) {
    // 1. 格式校验
    if (!idList?.length) {
      throw new Error('idList 不能为空');
    }
    //2. 删除用户
    const result = await this.userRepository.delete(idList);
    return {
      message: '删除成功',
      affected: result.affected,
    };
  }

  // 更新用户信息
  async updateUserInfoById(id: number, userInfo: UpdateUserDto) {
    // 1. 校验用户是否存在
    const userRecord = await this.userRepository.findOne({
      where: { id: id },
    });
    if (!userRecord) throw new Error('更新失败，用户不存在');
    // 2.  数据处理
    if (userInfo.hasOwnProperty('roles')) {
      userInfo.roles = JSON.stringify(userInfo.roles) as any;
    }
    userInfo.hasOwnProperty('password') && delete userInfo.password;
    // 3. 更新用户信息
    await this.verificationHistoryRepository.update(id, {
      ...userInfo,
    });
    return { message: '更新成功' };
  }

  // 重置密码
  async resetPassword(data: ResetPasswordDto) {
    console.log('resetPassword', data);
    // 1. 校验用户是否存在
    const userRecord = await this.userRepository.findOne({
      where: { username: data.username },
    });
    if (!userRecord) throw new Error('用户不存在');
    // 2. 校验验证码 有效性
    const record = await this.verificationHistoryRepository.findOne({
      where: {
        username: data.username,
        operationType: OperationType.resetPassword,
        isUsed: false,
      },
      order: { created_at: 'DESC' }, // 按 created_at 降序排列
    });
    if (
      !record ||
      record.verificationCode != data.captcha ||
      !isExpired(record.expiresAt)
    )
      throw new Error('验证码不正确或已过期');
    // 3. 对密码进行加密处理, 随机盐长度设为12
    data.password = bcryptjs.hashSync(data.password, 12);
    // 4. 更新用户信息
    await this.verificationHistoryRepository.update(record.id, {
      isUsed: true,
    });
    await this.userRepository.update(userRecord.id, {
      password: data.password,
    });
    return { message: '重置成功' };
  }

  //退出登录
  async logout() {
    // 注销token
    // const [err] = await to(
    //   this.redisService.deleteKey(`${userId}-${username}`),
    // );
    // if (err) throw new Error(err.message);
    return {
      message: '登出成功',
    };
  }
}

/* 工具函数 */
function isExpired(expiresAt: string): boolean {
  const currentTimestamp = new Date().getTime(); // 获取当前时间的毫秒级时间戳
  const expiresAtTimestamp = parseInt(expiresAt, 10); // 将字符串时间戳转换为数字

  return currentTimestamp <= expiresAtTimestamp;
}

function blockSensitiveInfo<T>(data: T, keys?: string[]): T {
  keys = keys ?? ['password'];
  if (Array.isArray(data)) {
    data.forEach((item: T) => {
      keys.forEach((key) => {
        item.hasOwnProperty(key) && delete item[key];
      });
    });
  } else {
    keys.forEach((key) => {
      data.hasOwnProperty(key) && delete data[key];
    });
  }
  return data;
}
